package productStatsTests;

import hitExceptions.InvalidDataException;
import hitExceptions.InvalidOperationException;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Random;
import java.util.SortedMap;
import java.util.TreeMap;

import coreModel.Barcode;
import coreModel.HIT;
import coreModel.Item;
import coreModel.Product;
import coreModel.StorageUnit;
import coreModel.Supply;
import coreModel.Unit;
import facades.ItemFacade;
import facades.ProductFacade;
import facades.StorageFacade;

import reports.builders.TestBuilder;
import reports.directors.ProductStatisticsReport;
import reports.directors.ProductStatsGenerator;
import tools.Importer;

public class DataGen 
{
	Random r = new Random();
	Product curProduct;
	Item curItem;
	SortedMap<String,Product> sortedProducts =
		new TreeMap<String,Product>(new noStringLeftBehind());
	int period;
	StorageUnit testSU;
	
	public DataGen()
	{
		//set up core model for testing
		Importer.main(new String[]{"test-src-tyguy555/import.xml"});
		HIT.getInstance().load();
		Collection<StorageUnit> suList = HIT.getInstance().getAllStorageUnits();
		for (StorageUnit su : suList)
		{
			System.out.println(su.getName());
			Collection<Product> pList = su.getProducts();
			for (Product p : pList)
			{
				System.out.println(p.getDescription());
			}
		}
					try	{
		testSU = new StorageUnit("testSU");
						}catch (InvalidDataException e) { testSU = null;}
		StorageFacade.getInstance().addStorageUnit(testSU);
		
		//set up default initial product and period
		addProduct();
		addItem();
		setPeriod(3);
	}
	
	public void setPeriod(int numMonths)
	{
		period = numMonths;
	}
	
	public void setDescription(String d)
	{
					try {
		curProduct.setDescription(d);
						} catch (InvalidDataException e) {e.printStackTrace();}
	}
	
	public void setThreeMonthSupply(int s)
	{
					try {
		curProduct.setThreeMonthSupply(s);
						} catch (InvalidDataException e) {e.printStackTrace();}
	}
	
	public void setBarcode(String b)
	{
		curProduct.setBarcode(new Barcode(b));
	}
	
	public void setSize(float s)
	{
					try {
		curProduct.setSize(s);
						} catch (InvalidDataException e) {e.printStackTrace();}
	}
	
	public void setUnit(Unit u)
	{
					try {
		curProduct.setUnit(u);
						} catch (InvalidDataException e) {e.printStackTrace();}
	}

	public void setCreationDate(Date d2) 
	{
					try {
		curProduct.setCreationDate(d2);
						} catch (InvalidDataException e) {e.printStackTrace();}
	}
	
	public void addProduct()
	{
		//save the old product (if there is one) in a sorted map for future reference
		if (curProduct != null)
			sortedProducts.put(curProduct.getDescription(), curProduct);
		
		//set up default initial product
		curProduct = newDefaultProduct();
		ProductFacade.getInstance().addProduct(curProduct, testSU);
	}
	
	private Product newDefaultProduct()
	{
		String description = "";
		
		for (int i=0; i<10 + r.nextInt(20); i++)
		{
			char randChar = (char) ('a' + r.nextInt(26));
			description += (r.nextInt(10) != 0)?(randChar):" "; 
		}
		
		String barcode = "";
		
		for (int i=0; i<16; i++)
		{
			barcode += (r.nextInt(10)); 
		}
		
		int threeMonthSupply = r.nextInt(10);
		
		int shelfLife = r.nextInt(25);
		
		Unit unit = Unit.values()[r.nextInt(10)];
		float size = ((unit == Unit.COUNT)?  1 : 1 + r.nextInt(100) / 10.0F);
		
				try {
		return new Product(new Barcode(barcode), description, 
							shelfLife, threeMonthSupply, new Supply(size, unit));
					} catch (InvalidDataException e) {e.printStackTrace(); return null;}
	}

	public void addItem() 
	{
		//set up default initial item
		curItem = newDefaultItem();
		ItemFacade.getInstance().addItem(curItem);
	}
	
	private Item newDefaultItem() 
	{
		try {
		Item i = new Item(new Barcode(), curProduct, testSU);
		i.setEntryDate(new Date());
		return i;
		} catch (InvalidDataException e) {e.printStackTrace(); return null;}
	}
	
	public TestBuilder getExpected()
	{
		TestBuilder expected = new TestBuilder();
		
		//save the last added product
		sortedProducts.put(curProduct.getDescription(), curProduct);
		
		for (Product p : sortedProducts.values())
		{
			ArrayList<String> row = new ArrayList<String>();
			
			row.add(p.getDescription());
			row.add(p.getBarcode().toString());
			
			float size = p.getSize();
			float sizeDecimal = size - ((int)size);
			String sizeS;
			if (sizeDecimal < 0.00001F)
				sizeS = "" + ((int)size);
			else
				sizeS = "" + size;
			row.add(sizeS + " " + p.getUnitAsString());
			
			row.add("" + ((p.getThreeMonthSupply()>0)?p.getThreeMonthSupply():""));
		
			//retrieve all saved items, even deleted ones
			ArrayList<Item> items = new ArrayList<Item>();
			Collection<Item> itemsInHIT = 
				HIT.getInstance().getItemManager().getAllItemsOfProduct(p);
			if (itemsInHIT != null)
				items.addAll(itemsInHIT);
			int cur = items.size();
			Collection<Item> itemsRemoved = 
				HIT.getInstance().getItemHistory().getRemovedItemsByProduct(p);
			if (itemsRemoved != null)
				items.addAll(itemsRemoved);
			float ave = 0;
			GregorianCalendar startOfPeriod = new GregorianCalendar();
			startOfPeriod.set(Calendar.HOUR_OF_DAY, 0);
			startOfPeriod.set(Calendar.MINUTE, 0);
			startOfPeriod.set(Calendar.SECOND, 0);
			startOfPeriod.set(Calendar.MILLISECOND, 0);
			GregorianCalendar dayInPeriod = (GregorianCalendar) startOfPeriod.clone();
			startOfPeriod.add(Calendar.MONTH, -(period));
			int daysInPeriod = 0;
			for (dayInPeriod.setTime(new Date()); startOfPeriod.before(dayInPeriod);
							dayInPeriod.add(Calendar.DAY_OF_MONTH, -1))
			{
				daysInPeriod++;
				for (Item item : items)
				{
					GregorianCalendar itemEntryDate = new GregorianCalendar();
					itemEntryDate.setTime(item.getEntryDate());
					GregorianCalendar itemExitDate = new GregorianCalendar();
					if (item.getExitTime() != null)
						itemExitDate.setTime(item.getExitTime());
					if (dayInPeriod.before(itemEntryDate) && 
							(item.getExitTime() == null || dayInPeriod.after(itemExitDate)))
						ave++;
				}
			}
			ave /= daysInPeriod;
			
			//format the average correctly (round to nearest .1, if whole, remove decimal places)
			ave += 0.05F;
			ave -= (ave*10 - (int)(ave*10))/10;
			float aveDecimal = ave - ((int)ave);
			if (aveDecimal < 0.00001F)
				row.add(cur + " / " + (int)ave);
			else
				row.add(cur + " / " + ave);
			
			
			//row.add("" + ((p.getShelfLife()>0)?p.getShelfLife():""));
			//TODO add other parts of a row
			
			expected.addRow(row);
		}

		System.out.println();
		System.out.println("Expected:");
		//output all rows generated for debugging purposes
		Collection<String> row;
		for (int i=0; i<expected.getRowCount(); i++)
		{
			row = expected.getRow(i);
			for (String s : row)
				System.out.print(s + " ");
			System.out.println();
		}
		
		return expected;
	}

	public TestBuilder getActual() 
	{
		//save the last added product
		sortedProducts.put(curProduct.getDescription(), curProduct);
		
		//run product stats report using TestBuilder
		ProductStatisticsReport reporter = new ProductStatisticsReport(period);
		TestBuilder actual = new TestBuilder();
		reporter.build(actual);
		return actual;
	}
	
	//this comparator lets a map with Key=String allow unique keys 
	private class noStringLeftBehind implements Comparator<String>
	{
		@Override
		public int compare(String o1, String o2) 
		{
			if (o1.compareTo(o2) == 0)
				return 1;
			else
				return o1.compareTo(o2);
		}
	}

	public void setEntryDate(Date d) 
	{
					try {
		curItem.setEntryDate(d);
						} catch (InvalidDataException e) {e.printStackTrace();}
	}

	public void setExitTime(Date d) 
	{
		ItemFacade.getInstance().removeItem(curItem);
					try {
		curItem.setExitTime(d);
						} catch (InvalidDataException e) {e.printStackTrace();}
	}
	
}
